#include "SkyNet.h"

static layer config[layer_count] = {
    {"conv0", 320, 160, 32, 320, 160, 32, 0, 0, 0},  //conv0
    {"conv1", 320, 160, 32, 320, 160, 32, 3, 1, 1},  //conv1
    {"conv2", 320, 160, 32, 320, 160, 64, 1, 1, 0},  //conv2
    {"pool1", 320, 160, 64, 160, 80, 64, 2, 2, 0},   //pool1
    {"conv3", 160, 80, 64, 160, 80, 64, 3, 1, 1},    //conv3
    {"conv4", 160, 80, 64, 160, 80, 96, 1, 1, 0},    //conv4
    {"pool2", 160, 80, 96, 80, 40, 96, 2, 2, 0},     //pool2
    {"conv5", 80, 40, 96, 80, 40, 96, 3, 1, 1},      //conv5
    {"conv6", 80, 40, 96, 80, 40, 192, 1, 1, 0},     //conv6
    {"reorg", 80, 40, 192, 40, 20, 768, 2, 2, 0},    //reorg
    {"pool3", 80, 40, 192, 40, 20, 192, 2, 2, 0},    //pool3
    {"conv7", 40, 20, 192, 40, 20, 192, 3, 1, 1},    //conv7
    {"conv8", 40, 20, 192, 40, 20, 384, 1, 1, 0},    //conv8
    {"conv9", 40, 20, 384, 40, 20, 384, 3, 1, 1},    //conv9
    {"conv10", 40, 20, 384, 40, 20, 512, 1, 1, 0},   //conv10
    {"cat", 40, 20, 192, 40, 20, 1280, 0, 0, 0},     //concat
    {"conv11", 40, 20, 1280, 40, 20, 1280, 3, 1, 1}, //conv11
    {"conv12", 40, 20, 1280, 40, 20, 96, 1, 1, 0},   //conv12
    {"conv13", 40, 20, 96, 40, 20, 32, 1, 1, 0},     //conv13
};

template<typename data_t>
void Array2Stream(const data_t array[32][43][83], hls::stream<data_t> stream[32])
{
    for (int w = 0; w < 83; w++)
    {
        for (int h = 0; h < 43; h++)
        {
#pragma HLS LOOP_FLATTEN
#pragma HLS PIPELINE II=1
            for (int c = 0; c < 32; c++)
            {
#pragma HLS UNROLL
                stream[c].write(array[c][h][w]);
            }
        }
    }
}
template<typename data_t>
void Stream2Array(hls::stream<data_t> stream[32], data_t array[32][43][83], bool clear)
{
    for (int w = 0; w < 83; w++)
    {
        for (int h = 0; h < 43; h++)
        {
#pragma HLS LOOP_FLATTEN
#pragma HLS PIPELINE II=1
            for (int c = 0; c < 32; c++)
            {
#pragma HLS UNROLL
                data_t data_s = stream[c].read();
                if (clear)
                {
                    array[c][h][w] = data_s;
                }
                else
                {
#pragma HLS LATENCY min=2
                    data_t data_a = array[c][h][w];
                    ap_int<20> data_r = data_s + data_a;
                    array[c][h][w] = data_r < smin ? data_t(smin) : data_r > smax ? data_t(smax) : data_t(data_r);
                }
            }
        }
    }
}
template<typename data_t>
void StreamAdd(hls::stream<data_t> stream_0[32], hls::stream<data_t> stream_1[32], hls::stream<data_t> stream_2[32])
{
    for (int w = 0; w < 83; w++)
    {
        for (int h = 0; h < 43; h++)
        {
#pragma HLS LOOP_FLATTEN
#pragma HLS PIPELINE II=2
            for (int c = 0; c < 32; c++)
            {
#pragma HLS UNROLL
#pragma HLS LATENCY min=2
                data_t data_0 = stream_0[c].read();
                data_t data_1 = stream_1[c].read();
                ap_int<20> data_2 = data_0 + data_1;
                data_t data_r = data_2 < smin ? data_t(smin) : data_2 > smax ? data_t(smax) : data_t(data_2);
                stream_2[c].write(data_2);
            }
        }
    }
}

template<typename data_t>
void StreamDup(hls::stream<data_t> stream_0[32], hls::stream<data_t> stream_1[32], hls::stream<data_t> stream_2[32])
{
    for (int w = 0; w < 83; w++)
    {
        for (int h = 0; h < 43; h++)
        {
#pragma HLS LOOP_FLATTEN
#pragma HLS PIPELINE II=1
            for (int c = 0; c < 32; c++)
            {
#pragma HLS UNROLL
                data_t data = stream_0[c].read();
                stream_1[c].write(data);
                stream_2[c].write(data);
            }
        }
    }
}

void REORG(ADT32 *ifm, hls::stream<ADT> IFM_stream[32], ap_uint<6> Cx, ap_uint<3> Rx)
{
    ap_uint<20> ifm_index;
    for (ap_uint<7> w = 0; w < 83; w++)
    {
        for (ap_uint<7> h = 0; h < 43; h++)
        {
#pragma HLS LOOP_FLATTEN
#pragma HLS PIPELINE II=1
            
            ap_uint<2> bias_h = (h >= 22) + (!Rx[1]);
            ap_uint<2> bias_w = (w >= 42) + (!Rx[0]);
            ap_uint<10> h_ = 2 * h - bias_h;
            ap_uint<10> w_ = 2 * w - bias_w;
            ap_uint<20> ifm_index = Cx * 83 * 163 + h_ * 163 + w_;

            ADT32 tmp_read = ifm[ifm_index];
            for (ap_uint<7> c = 0; c < 32; c++)
            {
#pragma HLS UNROLL
                if (h != 0 && h != 21 && h != 42 && w != 0 && w != 41 && w != 82)
                {
#ifndef DEBUG
                    IFM_stream[c].write(tmp_read.range(8 * c + 7, 8 * c));
#else
                    IFM_stream[c].write(tmp_read.data[c]);
#endif
                }
                else
                {
                    IFM_stream[c].write(-128);
                }
            }
        }
    }
}

void Load_WBUF3x3(WDT32 *weight, WDT WBUF3x3[32][3][3], ap_uint<6> Mx)
{
    for (ap_uint<4> m = 0; m < 3; m++)
    {
        for (ap_uint<4> n = 0; n < 3; n++)
        {
#pragma HLS PIPELINE II=1
            for (ap_uint<6> c = 0; c < 32; c++)
            {
#pragma HLS UNROLL
#ifndef DEBUG
                WBUF3x3[c][m][n] = (WDT)weight[Mx * 9 + m * 3 + n].range(8 * c + 7, 8 * c);
#else
                WBUF3x3[c][m][n] = (WDT)weight[Mx * 9 + m * 3 + n].data[c];
#endif
            }
        }
    }
}

void Load_WBUF1x1(WDT32 *weight, WDT WBUF1x1[32][32], ap_uint<6> Mx, ap_uint<6> Nx, ap_uint<12> ic)
{
    for (ap_uint<6> m = 0; m < 32; m++)
    {
#pragma HLS PIPELINE II=1
        for (ap_uint<6> n = 0; n < 32; n++)
        {
#pragma HLS UNROLL
#ifndef DEBUG
            WBUF1x1[m][n] = (WDT)weight[Mx * ic + Nx * 32 + m].range(8 * n + 7, 8 * n);
#else
            WBUF1x1[m][n] = (WDT)weight[Mx * ic + Nx * 32 + m].data[c];
#endif
        }
    }
}

void Load_BBUF(BDT8 *bias, BDT BBUF[32], ap_uint<6> Mx)
{
    for (ap_uint<5> i = 0; i < 4; i++)
    {
#pragma HLS PIPELINE II=1
        for (ap_uint<5> c = 0; c < 8; c++)
        {
#pragma HLS UNROLL
#ifndef DEBUG
            BBUF[i * 8 + c] = bias[Mx * 4 + i].range(32 * c + 18, 32 * c);
#else
            BBUF[i * 8 + c] = bias[Mx * 4 + i].data[c];
#endif
        }
    }
}

void Load_FM(ADT32 *ifm, hls::stream<ADT> IBUF[32], ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh)
{
    ap_uint<3> tile = ow / 80;
    ap_uint<10> h_offset, w_offset;
    if (tile)
    {
        h_offset = Hx * 40 + Hx / tile;
        w_offset = Wx * 80 + Wx / tile;
    }
    else
    {
        h_offset = 0;
        w_offset = 0;
    }
    
    for (ap_uint<8> w = 0; w < 83; w++)
    {
        for (ap_uint<8> h = 0; h < 43; h++)
        {
#pragma HLS PIPELINE II=1
            ap_uint<20> ifm_index = Cx * (oh * 2 + 3) * (ow * 2 + 3) + (h + h_offset) * (ow * 2 + 3) + (w + w_offset);

            ADT32 ifm_word = ifm[ifm_index];
            for (int c = 0; c < 32; c++)
            {
#ifndef DEBUG
                IBUF[c].write(ifm_word.range(8 * c + 7, 8 * c));
#else
                IBUF[c].write(ifm_word.data[c]);
#endif
            }
        }
    }
}

void Export_CONV(ADT32 *ofm, ADT OBUF[32][43][83], ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh)
{
    ap_uint<3> tile = ow / 80;
    ap_uint<10> h_offset, w_offset;
    if (tile)
    {
        h_offset = Hx * 40 + Hx / tile;
        w_offset = Wx * 80 + Wx / tile;
    }
    else
    {
        h_offset = 0;
        w_offset = 0;
    }
    for (int w = 0; w < 83; w++)
    {
        for (int h = 0; h < 43; h++)
        {
#pragma HLS PIPELINE II=1
            ap_uint<20> ofm_index = Cx * (oh * 2 + 3) * (ow * 2 + 3) + (h + h_offset) * (ow * 2 + 3) + (w + w_offset);
            for (int c = 0; c < 32; c++)
            {
                if (!(h == 0 || h == 42 || w == 0 || w == 82))
                {
#ifndef DEBUG
                    ofm[ofm_index].range(8 * c + 7, 8 * c) = OBUF[c][h][w];
#else
                    ofm[ofm_index].data[c] = OBUF[c][h][w];
#endif
                }
            }
        }
    }
}

void Export_CONV1(ADT32 *ofm, ADT OBUF[32][43][83], ap_uint<6> Cx)
{
    for (int h=0; h<43; h++)
    {
#pragma HLS PIPELINE II=1
        for (int c=0; c<32; c++)
        {
            OBUF[c][h][41] = -128;
        }
    }
    for (int w=0; w<83; w++)
    {
#pragma HLS PIPELINE II=1
        for (int c=0; c<32; c++)
        {
            OBUF[c][21][w] = -128;
        }
    }
    Export_CONV(ofm, OBUF, 0, 0, Cx, 40, 20);  
}

void Export_POOL(ADT32 *ofm, hls::stream<BDT> OBUF[32], ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh)
{
    ap_uint<3> tile = ow / 40;
    ap_uint<10> h_offset = Hx * 20 + Hx / tile;
    ap_uint<10> w_offset = Wx * 40 + Wx / tile;
    
    for (ap_uint<8> w = 1; w <= 40; w++)
    {
        for (ap_uint<8> h = 1; h <= 20; h++)
        {
#pragma HLS PIPELINE II=1
            ap_uint<20> ofm_index = Cx * (oh * 2 + 3) * (ow * 2 + 3) + (h + h_offset) * (ow * 2 + 3) + (w + w_offset);
            for (ap_uint<8> c = 0; c < 32; c++)
            {
                ofm[ofm_index].range(8 * c + 7, 8 * c) = OBUF[c].read();
            }
        }
    }
}

void Export_BBOX(BDT8 *bbox, BDT OBUF[32][43][83])
{
    for (ap_uint<3> c = 0; c < 2; c++)
    {
        for (ap_uint<8> h = 1; h < 43; h++)
        {
            for (ap_uint<8> w = 1; w < 83; w++)
            {
#pragma HLS PIPELINE II=1
                ap_uint<20> ofm_index = c * 43 * 83 + h * 83 + w;
                for (ap_uint<4> tm = 0; tm < 8; tm++)
                {
                    bbox[ofm_index].range(32 * tm + 31, 32 * tm) = OBUF[c * 8 + tm][h][w];
                }
            }
        }
    }
}

void PWCONV1X1(
    const ADT IFM[32][43][83],
    BDT OFM[32][43][83],
    const WDT WBUF1x1[32][32],
    bool clear
#ifdef USE_DSP
    ,
    bool ap_clk_div2
#endif
)
{
#pragma HLS STABLE variable=WBUF1x1
#pragma HLS DATAFLOW
    hls::stream<ADT> IFM_stream[32];
    hls::stream<BDT> OFM_stream[32];
    Array2Stream(IFM, IFM_stream);
    PWCONV1X1(IFM_stream, OFM_stream, WBUF1x1
#ifdef USE_DSP
    ,
    bool ap_clk_div2
#endif
    );
    Stream2Array(OFM_stream, OFM, clear);
}

void ACTIVATION(BDT IFM[32][43][83], ADT OFM[32][43][83], BDT BBUF[32], MDT MBUF[32])
{
#pragma HLS STABLE variable=BBUF
#pragma HLS STABLE variable=MBUF
#pragma HLS DATAFLOW
    hls::stream<BDT> IFM_stream[32];
    hls::stream<ADT> OFM_stream[32];
    Array2Stream(IFM, IFM_stream);
    ACTIVATION(IFM_stream, OFM_stream, BBUF, MBUF);
    Stream2Array(OFM_stream, OFM, true);
}

void dataflow_region_0(ADT32 *ifm, WDT WBUF3x3[32][3][3], BDT BBUF[32], MDT MBUF[32], ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh, ADT IFM[32][43][83])
{
#pragma HLS STABLE variable=WBUF3x3
#pragma HLS STABLE variable=BBUF
#pragma HLS STABLE variable=MBUF
#pragma HLS DATAFLOW

    hls::stream<ADT> datapath_0[32];
    hls::stream<BDT> datapath_1[32];
    hls::stream<ADT> datapath_2[32];
    Load_FM(ifm, datapath_0, Hx, Wx, Cx, ow, oh);
    DWCONV3X3(datapath_0, datapath_1, WBUF3x3);
    ACTIVATION(datapath_1, datapath_2, BBUF, MBUF);
    Stream2Array(datapath_2, IFM, true);
}

void dataflow_region_1(ADT IFM[32][43][83], WDT WBUF1x1[32][32], BDT BBUF[32], MDT MBUF[32], ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh, ADT32 *ofm)
{
#pragma HLS STABLE variable=WBUF1x1
#pragma HLS STABLE variable=BBUF
#pragma HLS STABLE variable=MBUF
#pragma HLS DATAFLOW

    hls::stream<ADT> datapath_0[32];
    hls::stream<BDT> datapath_1[32];
    hls::stream<ADT> datapath_2[32];
    hls::stream<BDT> datapath_3[32];
    Array2Stream(IFM, datapath_0);
    PWCONV1X1(datapath_0, datapath_1, WBUF1x1);
    ACTIVATION(datapath_1, datapath_2, BBUF, MBUF);
    POOL(datapath_2, datapath_3);
    Export_POOL(ofm + pool1_offset, datapath_3, Hx, Wx, Cx, ow, oh);
}

void dataflow_region_2(BDT OFM[32][43][83], BDT BBUF[32], MDT MBUF[32], ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh, ADT32 *ofm)
{
#pragma HLS STABLE variable=BBUF
#pragma HLS STABLE variable=MBUF
#pragma HLS DATAFLOW

    hls::stream<BDT> datapath_0[32];
    hls::stream<ADT> datapath_1[32];
    hls::stream<BDT> datapath_2[32];
    Array2Stream(OFM, datapath_0);
    ACTIVATION(datapath_0, datapath_1, BBUF, MBUF);
    POOL(datapath_1, datapath_2);
    Export_POOL(ofm + pool1_offset, datapath_2, Hx, Wx, Cx, ow, oh);
}

void dataflow_region_3(ADT32 *ifm, WDT WBUF1x1[32][32], bool clear, ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh, BDT OFM[32][43][83])
{
#pragma HLS STABLE variable=WBUF1x1
#pragma HLS DATAFLOW

    hls::stream<ADT> datapath_0[32];
    hls::stream<BDT> datapath_1[32];
    Load_FM(ifm, datapath_0, Hx, Wx, Cx, ow, oh);
    PWCONV1X1(datapath_0, datapath_1, WBUF1x1);
    Stream2Array(datapath_1, OFM, clear);
}

void dataflow_region_4(BDT OFM[32][43][83], BDT BBUF[32], MDT MBUF[32], ap_uint<6> Hx, ap_uint<6> Wx, ap_uint<6> Cx, ap_uint<10> ow, ap_uint<10> oh, ADT32 *ofm, ADT IFM[32][43][83])
{
#pragma HLS STABLE variable=BBUF
#pragma HLS STABLE variable=MBUF
#pragma HLS DATAFLOW

    hls::stream<BDT> datapath_0[32];
    hls::stream<ADT> datapath_1[32];
    hls::stream<ADT> datapath_2[32];
    hls::stream<ADT> datapath_3[32];
    hls::stream<BDT> datapath_4[32];
    Array2Stream(OFM, datapath_0);
    ACTIVATION(datapath_0, datapath_1, BBUF, MBUF);
    StreamDup(datapath_1, datapath_2, datapath_3);
    Stream2Array(datapath_2, IFM, true);
    POOL(datapath_3, datapath_4);
    Export_POOL(ofm + pool1_offset, datapath_4, Hx, Wx, Cx, ow, oh);
}

void dataflow_region_5(ADT32 *ifm, WDT WBUF3x3[32][3][3], BDT BBUF[32], MDT MBUF[32], ap_uint<6> Cx, ap_uint<3> Rx, ADT IFM[32][43][83])
{
#pragma HLS STABLE variable=WBUF3x3
#pragma HLS STABLE variable=BBUF
#pragma HLS STABLE variable=MBUF
#pragma HLS DATAFLOW

    hls::stream<ADT> datapath_0[32];
    hls::stream<BDT> datapath_1[32];
    hls::stream<ADT> datapath_2[32];
    REORG(ifm, datapath_0, Cx, Rx);
    DWCONV3X3(datapath_0, datapath_1, WBUF3x3);
    ACTIVATION(datapath_1, datapath_2, BBUF, MBUF);
    Stream2Array(datapath_2, IFM, true);
}

// #pragma SDS data zero_copy(ifm[0:12*323*643], ofm[0:1199181], weight[0:443344],biasm[0:428],bbox[0:43*83*4])
// #pragma SDS data mem_attribute("ifm":PHYSICAL_CONTIGUOUS,"ofm":PHYSICAL_CONTIGUOUS, "weight":PHYSICAL_CONTIGUOUS, "biasm":PHYSICAL_CONTIGUOUS, "bbox":PHYSICAL_CONTIGUOUS)
void SkyNet(ADT32 *ifm, ADT32 *ofm, WDT32 *weight, BDT8 *biasm, BDT8 *bbox)
{

#pragma HLS INTERFACE m_axi depth=207689 port=ifm offset=slave bundle=INPUT
#pragma HLS INTERFACE m_axi depth=620977 port=ofm offset=slave bundle=OUTPUT
#pragma HLS INTERFACE m_axi depth=443344 port=weight offset=slave bundle=INPUT
#pragma HLS INTERFACE m_axi depth=123 port=biasm offset=slave bundle=INPUT
#pragma HLS INTERFACE m_axi depth=8477 port=bbox offset=slave bundle=OUTPUT

#pragma HLS INTERFACE s_axilite register port=return

    ADT FM1[32][43][83];
#pragma HLS ARRAY_PARTITION variable=FM1 complete dim=1
#pragma HLS RESOURCE variable=FM1 core=RAM_2P_BRAM
    ADT FM2[32][43][83];
#pragma HLS ARRAY_PARTITION variable=FM2 complete dim=1
#pragma HLS RESOURCE variable=FM2 core=RAM_2P_BRAM
    ADT FM3[32][43][83];
#pragma HLS ARRAY_PARTITION variable=FM3 complete dim=1
#pragma HLS RESOURCE variable=FM3 core=RAM_2P_BRAM

    BDT FM5[32][43][83];
#pragma HLS ARRAY_PARTITION variable=FM5 complete dim=1

    WDT WBUF3x3[3][32][3][3];
#pragma HLS ARRAY_PARTITION variable=WBUF3x3 complete dim=2

    WDT WBUF1x1_0[32][32];
#pragma HLS ARRAY_PARTITION variable=WBUF1x1_0 complete dim=0
    WDT WBUF1x1_1[32][32];
#pragma HLS ARRAY_PARTITION variable=WBUF1x1_1 complete dim=0

    BDT BBUF_0[32];
#pragma HLS ARRAY_PARTITION variable=BBUF_0 complete dim=0
    BDT BBUF_1[32];
#pragma HLS ARRAY_PARTITION variable=BBUF_1 complete dim=0
    BDT BBUF_2[32];
#pragma HLS ARRAY_PARTITION variable=BBUF_2 complete dim=0
    MDT MBUF_0[32];
#pragma HLS ARRAY_PARTITION variable=MBUF_0 complete dim=0
    MDT MBUF_1[32];
#pragma HLS ARRAY_PARTITION variable=MBUF_1 complete dim=0
    MDT MBUF_2[32];
#pragma HLS ARRAY_PARTITION variable=MBUF_2 complete dim=0

#pragma HLS ALLOCATION instances=Load_WBUF3x3 limit=1 function
#pragma HLS ALLOCATION instances=Load_BBUF limit=1 function
#pragma HLS ALLOCATION instances=Load_WBUF1x1 limit=1 function
#pragma HLS ALLOCATION instances=Load_FM limit=1 function
#pragma HLS ALLOCATION instances=Load_FM1 limit=1 function
#pragma HLS ALLOCATION instances=DWCONV3X3 limit=1 function
#pragma HLS ALLOCATION instances=ACTIVATION limit=1 function
#pragma HLS ALLOCATION instances=PWCONV1X1 limit=1 function
#pragma HLS ALLOCATION instances=POOL limit=1 function
#pragma HLS ALLOCATION instances=Export_POOL limit=1 function
#pragma HLS ALLOCATION instances=Export_CONV limit=1 function
#pragma HLS ALLOCATION instances=Export_CONV1 limit=1 function

    /*********************************DWCONV1+PWCONV1********************************/
    // std::cout << "DWCONV1+PWCONV1" << std::endl;
    Load_WBUF3x3(weight + conv1_w, WBUF3x3[0], 0);
    Load_BBUF(biasm + conv1_b, BBUF_0, 0);
    Load_BBUF(biasm + conv1_m, MBUF_0, 0);

    Load_WBUF1x1(weight + conv2_w, WBUF1x1_0, 0, 0, config[2].ic);
    Load_WBUF1x1(weight + conv2_w, WBUF1x1_1, 1, 0, config[2].ic);
    Load_BBUF(biasm + conv2_b, BBUF_1, 0);
    Load_BBUF(biasm + conv2_b, BBUF_2, 1);
    Load_BBUF(biasm + conv2_m, MBUF_1, 0);
    Load_BBUF(biasm + conv2_m, MBUF_2, 1);
    {
        for (ap_uint<6> Hx = 0; Hx < 8; Hx++)
        {
            std::cout << "  Hx: " << Hx << std::endl;
            for (ap_uint<6> Wx = 0; Wx < 8; Wx++)
            {
                std::cout << "      Wx: " << Wx << std::endl;
                dataflow_region_0(ifm, WBUF3x3[0], BBUF_0, MBUF_0, Hx, Wx, 0, config[0].ow, config[0].oh, FM1);
                std::cout << "          Cx: " << 0 << std::endl;
                dataflow_region_1(FM1, WBUF1x1_0, BBUF_1, MBUF_1, Hx, Wx, 0, config[3].ow, config[3].oh, ofm);
                std::cout << "          Cx: " << 1 << std::endl;
                dataflow_region_1(FM1, WBUF1x1_1, BBUF_2, MBUF_2, Hx, Wx, 1, config[3].ow, config[3].oh, ofm);
            }
        }
    }
    /*********************************DWCONV2+PWCONV2********************************/
    std::cout << "DWCONV2+PWCONV2" << std::endl;
    Load_WBUF3x3(weight + conv3_w, WBUF3x3[0], 0);
    Load_WBUF3x3(weight + conv3_w, WBUF3x3[1], 1);
    Load_BBUF(biasm + conv3_b, BBUF_0, 0);
    Load_BBUF(biasm + conv3_b, BBUF_1, 1);
    Load_BBUF(biasm + conv3_m, MBUF_0, 0);
    Load_BBUF(biasm + conv3_m, MBUF_1, 1);
    {
        for (ap_uint<6> Hx = 0; Hx < 4; Hx++)
        {
            std::cout << "  Hx: " << Hx << std::endl;
            for (ap_uint<6> Wx = 0; Wx < 4; Wx++)
            {
                std::cout << "      Wx: " << Wx << std::endl;
                dataflow_region_0(ofm + pool1_offset, WBUF3x3[0], BBUF_0, MBUF_0, Hx, Wx, 0, config[4].ow, config[4].oh, FM1);
                dataflow_region_0(ofm + pool1_offset, WBUF3x3[1], BBUF_1, MBUF_1, Hx, Wx, 1, config[4].ow, config[4].oh, FM2);

                for (ap_uint<6> Mx = 0; Mx < 3; Mx++)
                {
                    std::cout << "          Mx: " << Mx << std::endl;
                    Load_WBUF1x1(weight + conv4_w, WBUF1x1_0, Mx, 0, config[5].ic);
                    PWCONV1X1(FM1, FM5, WBUF1x1_0, true);

                    Load_WBUF1x1(weight + conv4_w, WBUF1x1_1, Mx, 1, config[5].ic);
                    PWCONV1X1(FM2, FM5, WBUF1x1_1, false);

                    Load_BBUF(biasm + conv4_b, BBUF_2, Mx);
                    Load_BBUF(biasm + conv4_m, MBUF_2, Mx);

                    dataflow_region_2(FM5, BBUF_2, MBUF_2, Hx, Wx, Mx, config[6].ow, config[6].oh, ofm + pool2_offset);
                }
            }
        }
    }
    /*********************************DWCONV3+PWCONV3********************************/
    std::cout << "DWCONV3+PWCONV3" << std::endl;
    Load_WBUF3x3(weight + conv5_w, WBUF3x3[0], 0);
    Load_WBUF3x3(weight + conv5_w, WBUF3x3[1], 1);
    Load_WBUF3x3(weight + conv5_w, WBUF3x3[2], 2);
    Load_BBUF(biasm + conv5_b, BBUF_0, 0);
    Load_BBUF(biasm + conv5_b, BBUF_1, 1);
    Load_BBUF(biasm + conv5_b, BBUF_2, 2);
    Load_BBUF(biasm + conv5_m, MBUF_0, 0);
    Load_BBUF(biasm + conv5_m, MBUF_1, 1);
    Load_BBUF(biasm + conv5_m, MBUF_2, 2);
    {
        for (ap_uint<6> Hx = 0; Hx < 2; Hx++)
        {
            std::cout << "  Hx: " << Hx << std::endl;
            for (ap_uint<6> Wx = 0; Wx < 2; Wx++)
            {
                std::cout << "      Wx: " << Wx << std::endl;
                dataflow_region_0(ofm + pool2_offset, WBUF3x3[0], BBUF_0, MBUF_0, Hx, Wx, 0, config[7].ow, config[7].oh, FM1);
                Export_CONV(ofm + conv5_offset, FM1, Hx, Wx, 0, config[7].ow, config[7].oh);

                dataflow_region_0(ofm + pool2_offset, WBUF3x3[1], BBUF_1, MBUF_1, Hx, Wx, 1, config[7].ow, config[7].oh, FM1);
                Export_CONV(ofm + conv5_offset, FM1, Hx, Wx, 1, config[7].ow, config[7].oh);

                dataflow_region_0(ofm + pool2_offset, WBUF3x3[2], BBUF_2, MBUF_2, Hx, Wx, 2, config[7].ow, config[7].oh, FM1);
                Export_CONV(ofm + conv5_offset, FM1, Hx, Wx, 2, config[7].ow, config[7].oh);
            }
        }
    }
    {
        for (ap_uint<6> Hx = 0; Hx < 2; Hx++)
        {
            std::cout << "  Hx: " << Hx << std::endl;
            for (ap_uint<6> Wx = 0; Wx < 2; Wx++)
            {
                std::cout << "      Wx: " << Wx << std::endl;
                for (ap_uint<6> Mx = 0; Mx < 6; Mx++)
                {
                    std::cout << "          Mx: " << Mx << std::endl;
                    Load_WBUF1x1(weight + conv6_w, WBUF1x1_0, Mx, 0, config[8].ic);
                    dataflow_region_3(ofm + conv5_offset, WBUF1x1_0, true, Hx, Wx, 0, config[7].ow, config[7].oh, FM5);

                    Load_WBUF1x1(weight + conv6_w, WBUF1x1_0, Mx, 1, config[8].ic);
                    dataflow_region_3(ofm + conv5_offset, WBUF1x1_0, false, Hx, Wx, 1, config[7].ow, config[7].oh, FM5);

                    Load_WBUF1x1(weight + conv6_w, WBUF1x1_0, Mx, 2, config[8].ic);
                    dataflow_region_3(ofm + conv5_offset, WBUF1x1_0, false, Hx, Wx, 2, config[7].ow, config[7].oh, FM5);

                    Load_BBUF(biasm + conv6_b, BBUF_0, Mx);
                    Load_BBUF(biasm + conv6_m, MBUF_0, Mx);

                    dataflow_region_4(FM5, BBUF_0, MBUF_0, Hx, Wx, Mx, config[10].ow, config[10].oh, ofm + pool3_offset, FM1);
                    Export_CONV(ofm + conv6_offset, FM1, Hx, Wx, Mx, config[8].ow, config[8].oh);
                }
            }
        }
    }
    /*********************************DWCONV4+PWCONV4********************************/
    std::cout << "DWCONV4+PWCONV4" << std::endl;
    {
        for(int Nx=0; Nx<6; Nx++)
        {
            std::cout << "  Nx: " << Nx << std::endl;
            Load_WBUF3x3(weight + conv7_w, WBUF3x3[0], Nx);
            Load_BBUF(biasm + conv7_b, BBUF_0, Nx);
            Load_BBUF(biasm + conv7_m, MBUF_0, Nx);

            dataflow_region_0(ofm + pool3_offset, WBUF3x3[0], BBUF_0, MBUF_0, 0, 0, Nx, 40, 20, FM1);
            Export_CONV1(ofm + conv7_offset, FM1, Nx);
        }
    }
    {
        for(int Mx=0; Mx<12; Mx++)
        {
            std::cout << "  Mx: " << Mx << std::endl;
            Load_BBUF(biasm + conv8_b, BBUF_0, Mx);
            Load_BBUF(biasm + conv8_m, MBUF_0, Mx);
            for(int Nx=0; Nx<6; Nx++)
            {
                std::cout << "      Nx: " << Nx << std::endl;
                Load_WBUF1x1(weight + conv8_w, WBUF1x1_0, Mx, Nx, config[12].ic);
                dataflow_region_3(ofm + conv7_offset, WBUF1x1_0, Nx == 0, 0, 0, Nx, 40, 20, FM5);
            }
            ACTIVATION(FM5, FM1, BBUF_0, MBUF_0);
            Export_CONV1(ofm + conv8_offset, FM1, Mx);
        }
    }
    /*********************************DWCONV5+PWCONV5********************************/
    std::cout << "DWCONV5+PWCONV5" << std::endl;
    {
        for(int Nx=0; Nx<12; Nx++)
        {
            std::cout << "  Nx: " << Nx << std::endl;
            Load_WBUF3x3(weight + conv9_w, WBUF3x3[0], Nx);
            Load_BBUF(biasm + conv9_b, BBUF_0, Nx);
            Load_BBUF(biasm + conv9_m, MBUF_0, Nx);

            dataflow_region_0(ofm + conv8_offset, WBUF3x3[0], BBUF_0, MBUF_0, 0, 0, Nx, 40, 20, FM1);
            Export_CONV1(ofm + conv9_offset, FM1, Nx);
        }
    }
    {
        for(int Mx=0; Mx<16; Mx++)
        {
            std::cout << "  Mx: " << Mx << std::endl;
            Load_BBUF(biasm + conv10_b, BBUF_0, Mx);
            Load_BBUF(biasm + conv10_m, MBUF_0, Mx);
            for(int Nx=0; Nx<12; Nx++)
            {
                std::cout << "      Nx: " << Nx << std::endl;
                Load_WBUF1x1(weight + conv10_w, WBUF1x1_0, Mx, Nx, config[14].ic);
                dataflow_region_3(ofm + conv9_offset, WBUF1x1_0, Nx == 0, 0, 0, Nx, 40, 20, FM5);
            }
            ACTIVATION(FM5, FM1, BBUF_0, MBUF_0);
            Export_CONV1(ofm + conv10_offset, FM1, Mx);
        }
    }
    /*********************************REORG+CONCAT+DWCONV6********************************/
    std::cout << "REORG+CONCAT+DWCONV6" << std::endl;
    {
        for (ap_uint<6> Nx = 0; Nx < 6; Nx++)
        {
            std::cout << "  Nx: " << Nx << std::endl;
            for(int Rx=0; Rx<4; Rx++)
            {
                std::cout << "      Rx: " << Rx << std::endl;
                Load_WBUF3x3(weight + conv11_w, WBUF3x3[0], Nx+Rx*6);
                Load_BBUF(biasm + conv11_b, BBUF_0, Nx+Rx*6);
                Load_BBUF(biasm + conv11_m, MBUF_0, Nx+Rx*6);

                dataflow_region_5(ofm + conv6_offset, WBUF3x3[0], BBUF_0, MBUF_0, Nx, Rx, FM1);
                
                Export_CONV1(ofm + conv11_offset, FM1, Nx+Rx*6);
            }
        }
    }
    {
        for(int Nx=0; Nx<16; Nx++)
        {
            std::cout << "  Nx: " << Nx << std::endl;
            Load_WBUF3x3(weight + conv11_w, WBUF3x3[0], Nx+24);
            Load_BBUF(biasm + conv11_b, BBUF_0, Nx+24);
            Load_BBUF(biasm + conv11_m, MBUF_0, Nx+24);

            dataflow_region_0(ofm + conv10_offset, WBUF3x3[0], BBUF_0, MBUF_0, 0, 0, Nx, 40, 20, FM1);
            Export_CONV1(ofm + conv11_offset, FM1, Nx+24);
        }
    }
    /*********************************PWCONV6********************************/
    std::cout << "PWCONV6" << std::endl;
    {
        for(int Mx=0; Mx<3; Mx++)
        {
            std::cout << "  Mx: " << Mx << std::endl;
            for(int Nx=0; Nx<40; Nx++)
            {
                // std::cout << "      Nx: " << Nx << std::endl;
                Load_WBUF1x1(weight + conv12_w, WBUF1x1_0, Mx, Nx, config[17].ic);
                dataflow_region_3(ofm + conv11_offset, WBUF1x1_0, Nx == 0, 0, 0, Nx, 40, 20, FM5);
            }
            Load_BBUF(biasm + conv12_b, BBUF_0, Mx);
            Load_BBUF(biasm + conv12_m, MBUF_0, Mx);
            ACTIVATION(FM5, FM1, BBUF_0, MBUF_0);
            Export_CONV1(ofm + conv12_offset, FM1, Mx);
        }
    }
    /*********************************CONV13********************************/
    std::cout << "CONV13" << std::endl;
    for (ap_uint<6> Nx = 0; Nx < 3; Nx++)
    {
        std::cout << "Nx: " << Nx << std::endl;
        Load_WBUF1x1(weight + conv13_w, WBUF1x1_0, 0, Nx, config[18].ic);
        dataflow_region_3(ofm + conv12_offset, WBUF1x1_0, Nx == 0, 0, 0, Nx, 40, 20, FM5);
    }
    Export_BBOX(bbox, FM5);
}
